//
// Created on 2025/6/16.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "program_cl.h"

ProgramCl::ProgramCl() { OH_LOG_INFO(LOG_APP, "ProgramCl constructor called."); }
ProgramCl::~ProgramCl() { OH_LOG_INFO(LOG_APP, "ProgramCl destructor called."); }

void ProgramCl::runMethod() {
    try {
        cl_int err_code = CL_SUCCESS;
        //     1. 得到可用平台
        std::vector<cl::Platform> platforms;
        cl::Platform::get(&platforms);
        if (platforms.size() == 0) {
            OH_LOG_INFO(LOG_APP, "platform size 0.");
        }
        OH_LOG_INFO(LOG_APP, "platform size %{public}lu.", platforms.size());
        //     2. 如果可以找到相应平台，就使用它
        cl_context_properties properties[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0};
        //     3. 生成上下文
        cl::Context context(CL_DEVICE_TYPE_GPU, properties);
        //     4. 寻找opencl设备，首先得到设备列表的长度，并获取设备列表
        std::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
        //     5. 创建一个oepncl command queue
        cl::CommandQueue queue(context, devices[0], 0, &err_code);
        //     6. 得到指定名字的内核实例句柄
        std::string program_kernel = loadProgram(filePath);
        cl::Program::Sources sources;

        if (program_kernel.empty())
            sources = cl::Program::Sources(1, std::make_pair(program_source, (strlen(program_source) + 1)));
        else
            sources = cl::Program::Sources(1, std::make_pair(program_kernel.c_str(), (program_kernel.length() + 1)));

        //     7. 装载内核程序，编译cl program，生成cl内核实例
        cl::Program program(context, sources);
        //     8. 为所有指定的设备生成cl program
        const char *options = "-cl-fast-relaxed-math";
        program.build(devices, options);
        //      9. 得到指定名字的内核实例的句柄
        cl::Kernel kernel(program, "add_two_numbers", &err_code);

        cl::Buffer bufferAdd1(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, N * sizeof(int), add1.data());
        cl::Buffer bufferAdd2(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, N * sizeof(int), add2.data());
        cl::Buffer bufferResult(context, CL_MEM_WRITE_ONLY, N * sizeof(int));

        kernel.setArg(0, bufferAdd1);
        kernel.setArg(1, bufferAdd2);
        kernel.setArg(2, bufferResult);

        cl::Event event;

        queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(N), cl::NullRange, NULL, &event);
        queue.finish();

        queue.enqueueReadBuffer(bufferResult, CL_TRUE, 0, sizeof(int) * N, result.data());

        OH_LOG_INFO(LOG_APP, "Result: %{public}d", result[0]);
    } catch (cl::Error err1) {
        OH_LOG_ERROR(LOG_APP, "try catch err is %{public}s.", err1.what());
    }
}

void ProgramCl::setFilePath(const std::string &path) {
    // 验证路径非空
    if (path.empty()) {
        OH_LOG_ERROR(LOG_APP, "ProgramCl attempt to set empty file path");
        return;
    }

    // 确保路径不以斜杠结尾
    std::string normalizedPath = path;
    if (normalizedPath.back() == '/') {
        normalizedPath.pop_back();
    }

    // 添加/test.cl后缀
    filePath = normalizedPath + "/add_two_numbers.cl";

    OH_LOG_DEBUG(LOG_APP, "ProgramCl set file path to: %{public}s", filePath.c_str());
}

const std::string &ProgramCl::getFilePath() const { return filePath; }